package com.mindskip.xzs.controller.admin;

import com.mindskip.xzs.base.BaseApiController;
import com.mindskip.xzs.base.RestResponse;
import com.mindskip.xzs.base.SystemCode;
import com.mindskip.xzs.domain.enums.RoleEnum;
import com.mindskip.xzs.domain.other.KeyValue;
import com.mindskip.xzs.domain.User;
import com.mindskip.xzs.domain.UserEventLog;
import com.mindskip.xzs.domain.enums.UserStatusEnum;
import com.mindskip.xzs.service.AuthenticationService;
import com.mindskip.xzs.service.UserEventLogService;
import com.mindskip.xzs.service.UserService;
import com.mindskip.xzs.utility.DateTimeUtil;
import com.mindskip.xzs.utility.POIUtil;
import com.mindskip.xzs.viewmodel.admin.user.*;
import com.mindskip.xzs.utility.PageInfoHelper;
import com.github.pagehelper.PageInfo;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

@RestController("AdminUserController")
@RequestMapping(value = "/api/admin/user")
public class UserController extends BaseApiController {

    private final UserService userService;
    private final UserEventLogService userEventLogService;
    private final AuthenticationService authenticationService;

    @Autowired
    public UserController(UserService userService, UserEventLogService userEventLogService, AuthenticationService authenticationService) {
        this.userService = userService;
        this.userEventLogService = userEventLogService;
        this.authenticationService = authenticationService;
    }


    @RequestMapping(value = "/page/list", method = RequestMethod.POST)
    public RestResponse<PageInfo<UserResponseVM>> pageList(@RequestBody UserPageRequestVM model) {
        PageInfo<User> pageInfo = userService.userPage(model);
        PageInfo<UserResponseVM> page = PageInfoHelper.copyMap(pageInfo, d -> UserResponseVM.from(d));
        return RestResponse.ok(page);
    }


    @RequestMapping(value = "/event/page/list", method = RequestMethod.POST)
    public RestResponse<PageInfo<UserEventLogVM>> eventPageList(@RequestBody UserEventPageRequestVM model) {
        PageInfo<UserEventLog> pageInfo = userEventLogService.page(model);
        PageInfo<UserEventLogVM> page = PageInfoHelper.copyMap(pageInfo, d -> {
            UserEventLogVM vm = modelMapper.map(d, UserEventLogVM.class);
            vm.setCreateTime(DateTimeUtil.dateFormat(d.getCreateTime()));
            return vm;
        });
        return RestResponse.ok(page);
    }

    @RequestMapping(value = "/select/{id}", method = RequestMethod.POST)
    public RestResponse<UserResponseVM> select(@PathVariable Integer id) {
        User user = userService.getUserById(id);
        UserResponseVM userVm = UserResponseVM.from(user);
        return RestResponse.ok(userVm);
    }

    @RequestMapping(value = "/current", method = RequestMethod.POST)
    public RestResponse<UserResponseVM> current() {
        User user = getCurrentUser();
        UserResponseVM userVm = UserResponseVM.from(user);
        return RestResponse.ok(userVm);
    }


    @RequestMapping(value = "/edit", method = RequestMethod.POST)
    public RestResponse<User> edit(@RequestBody @Valid UserCreateVM model) {
        if (model.getId() == null) {  //create
            User existUser = userService.getUserByUserName(model.getUserName());
            if (null != existUser) {
                return new RestResponse<>(2, "用户已存在");
            }

            if (StringUtils.isBlank(model.getPassword())) {
                return new RestResponse<>(3, "密码不能为空");
            }
        }
        if (StringUtils.isBlank(model.getBirthDay())) {
            model.setBirthDay(null);
        }
        User user = modelMapper.map(model, User.class);

        if (model.getId() == null) {
            String encodePwd = authenticationService.pwdEncode(model.getPassword());
            user.setPassword(encodePwd);
            user.setUserUuid(UUID.randomUUID().toString());
            user.setCreateTime(new Date());
            user.setLastActiveTime(new Date());
            user.setDeleted(false);
            userService.insertByFilter(user);
        } else {
            if (!StringUtils.isBlank(model.getPassword())) {
                String encodePwd = authenticationService.pwdEncode(model.getPassword());
                user.setPassword(encodePwd);
            }
            user.setModifyTime(new Date());
            userService.updateByIdFilter(user);
        }
        return RestResponse.ok(user);
    }

    /**
     * excel导入
     * @return
     */
    @RequestMapping(value = "/excelImport", method = RequestMethod.POST)
    public RestResponse<String> excelImport(@RequestBody MultipartFile file) throws IOException, ParseException {
        //1.读取excel中的数据并去除空行
        List<String[]> excelRows = readExcel(file);
        excelRows.removeAll(Collections.singleton(null));
        //2.对excel表中所有的学生信息进行校验，返回校验结果。
        if (excelRows.size()>0){
            HashMap<String,Object> validateExcelResult = validateExcel(excelRows);
            //3.全部校验成功后上传，校验失败则不上传，返回错误代码。
            if ((boolean)validateExcelResult.get("flag")==true){
                //批量插入学生信息
                for (int i=0;i<excelRows.size();i++) {
                    String[] row = excelRows.get(i);
                    User existUser = userService.getUserByUserName(row[0]);
                    if (null != existUser) {
                        return RestResponse.fail(SystemCode.ParameterValidError.getCode(),existUser.getUserName()+"用户已经存在");
                    }
                    insertOneStudent(row);
                }
            }else{
                String errorMsg = validateExcelResult.get("errorMsg").toString();
                return RestResponse.fail(SystemCode.ParameterValidError.getCode(),errorMsg);
            }
        }else {
            return RestResponse.fail(SystemCode.ParameterValidError.getCode(),"excel文件为空");
        }
        return RestResponse.ok("导入成功！");
    }

    private void insertOneStudent(String[] row) throws ParseException {
        User user = new User();
        user.setUserUuid(UUID.randomUUID().toString());
        user.setUserName(row[0]);
        user.setRealName(row[2]);
        user.setAge(Integer.parseInt(row[3]));
        user.setSex((row[4].equals("男")?1:2));
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        user.setBirthDay(sdf.parse(row[5]));
        user.setPassword(authenticationService.pwdEncode(row[1]));
        user.setPhone(row[6]);
        user.setUserLevel(Integer.parseInt(row[7]));
        user.setUserSubject(Integer.parseInt(row[8]));
        user.setRole(RoleEnum.STUDENT.getCode());
        user.setStatus(UserStatusEnum.Enable.getCode());
        user.setImagePath("https://www.mindskip.net:9008/image/ba607a75-83ba-4530-8e23-660b72dc4953/头像.jpg");
        user.setLastActiveTime(new Date());
        user.setCreateTime(new Date());
        user.setDeleted(false);
        userService.insertByFilter(user);
    }

    /**
     * 读入excel文件，解析后返回
     *
     * @param file
     * @throws IOException
     */
    public static List<String[]> readExcel(MultipartFile file) throws IOException {
        //检查文件
        POIUtil.checkFile(file);
        //获得Workbook工作薄对象
        Workbook workbook = POIUtil.getWorkBook(file);
        //创建返回对象，把每行中的值作为一个数组，所有行作为一个集合返回
        List<String[]> list = new ArrayList<String[]>();
        if (workbook != null) {
            for (int sheetNum = 0; sheetNum < workbook.getNumberOfSheets(); sheetNum++) {
                //获得当前sheet工作表
                Sheet sheet = workbook.getSheetAt(sheetNum);
                if (sheet == null) {
                    continue;
                }
                //获得当前sheet的开始行
                int firstRowNum = sheet.getFirstRowNum();
                //获得当前sheet的结束行
                int lastRowNum = sheet.getLastRowNum();
                //循环除了第一行的所有行
                for (int rowNum = firstRowNum + 1; rowNum <= lastRowNum; rowNum++) {
                    //获得当前行
                    Row row = sheet.getRow(rowNum);
                    if (row == null) {
                        continue;
                    }
                    //获得当前行的开始列
                    int firstCellNum = row.getFirstCellNum();
                    //获得当前行的列数
                    int lastCellNum = row.getLastCellNum();
                    String[] cells = new String[lastCellNum];
                    //循环当前行
                    for (int cellNum = firstCellNum; cellNum < lastCellNum; cellNum++) {
                        Cell cell = row.getCell(cellNum);
                        cells[cellNum] = POIUtil.getCellValue(cell);
                    }
                    list.add(cells);
                }
            }
            workbook.close();
        }
        return list;
    }

    /**
     * 对excel中的数据有效性进行校验
     *
     * @param excelRows excel中所有行的集合
     */
    public HashMap<String,Object> validateExcel(List<String[]> excelRows){
        HashMap<String,Object> resultMap=new HashMap<String,Object>();
        for (int i=1;i<=excelRows.size();i++) {
            String[] row = excelRows.get(i-1);

            //excel中必填的属性
            HashMap<Integer, String> map = new HashMap<>();
            map.put(1,"学生账号");
            map.put(2,"学生密码");
            map.put(3,"真实姓名");
            map.put(4,"性别");

            //分别校验excel中当前行必填的属性是否为空
            for (int j = 0; j < map.size(); j++) {
                //校验表格数据行中必填项是否为空(账号)
                if (StringUtils.isBlank(row[j])) {
                    resultMap.put("flag",false);
                    resultMap.put("errorMsg","excel表格第"+(i+1)+"行记录中，"+map.get(j+1)+"为空。");
                    return resultMap;
                }
            }

            //校验表格数据行中学生生日是否非法
            if (row[5]!=null && !checkDate(row[5])) {
                resultMap.put("flag",false);
                resultMap.put("errorMsg","excel表格第"+(i+1)+"行学生生日不合理。");
                return resultMap;
            }

            //校验计划班期是否合理(字符串中只允许出现男或者女)
            String sex = row[4];
            if(StringUtils.isNoneBlank(sex)){
                boolean sexLegal = ("男".equals(sex) || "女".equals(sex));
                if (!sexLegal){
                    resultMap.put("flag",false);
                    resultMap.put("errorMsg","excel表格第"+(i+1)+"行性别含有非法字符。");
                    return resultMap;
                }
            }
            //校验手机号
            String phone = row[6];
            if (StringUtils.isNoneBlank(phone)){
                String phoneRegex = "^1[3456789]\\d{9}$";
                if (phone.length()!=11 || !phone.matches(phoneRegex)){
                    resultMap.put("flag",false);
                    resultMap.put("errorMsg","第"+(i+1)+"行的手机号错误。");
                    return resultMap;
                }
            }
        }
        resultMap.put("flag",true);
        return resultMap;
    }

    /**
     * 获取学生信息上传模板
     *
     * @param response
     * @throws Exception
     */
    @RequestMapping(value = "download_template", method = RequestMethod.GET)
    @ResponseBody
    public void downloadTemplate(HttpServletResponse response) throws Exception {
        response.setHeader("content-type", "application/octet-stream");
        response.setContentType("application/octet-stream");
        // 下载文件能正常显示中文
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("学生信息上传模板.xlsx", "UTF-8"));
        ServletOutputStream out = response.getOutputStream();

        ClassPathResource resource = new ClassPathResource("/excelTemplate/studentExcel.xlsx");
        InputStream in = resource.getInputStream();
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024 * 8];
        int n;
        while (-1 != (n = in.read(buffer))) {
            output.write(buffer, 0, n);
        }
        byte[] bytes = output.toByteArray();
        response.setContentLength(bytes.length);
        out.write(bytes);
        output.close();
        out.close();
        in.close();
    }

    /**
     * 对日期有效性进行校验（包括闰年检验及时间格式检验）
     * @param str 格式为yyyy-MM-dd
     */
    public boolean checkDate (String str) {
        SimpleDateFormat sd=new SimpleDateFormat("yyyy-MM-dd");//括号内为日期格式，y代表年份，M代表年份中的月份（为避免与小时中的分钟数m冲突，此处用M），d代表月份中的天数
        try {
            sd.setLenient(false);//此处指定日期/时间解析是否不严格，在true是不严格，false时为严格
            sd.parse(str);//从给定字符串的开始解析文本，以生成一个日期
        }
        catch (Exception e) {
            return false;
        }
        return true;
    }

    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public RestResponse update(@RequestBody @Valid UserUpdateVM model) {
        User user = userService.selectById(getCurrentUser().getId());
        modelMapper.map(model, user);
        user.setModifyTime(new Date());
        userService.updateByIdFilter(user);
        return RestResponse.ok();
    }


    @RequestMapping(value = "/changeStatus/{id}", method = RequestMethod.POST)
    public RestResponse<Integer> changeStatus(@PathVariable Integer id) {
        User user = userService.getUserById(id);
        UserStatusEnum userStatusEnum = UserStatusEnum.fromCode(user.getStatus());
        Integer newStatus = userStatusEnum == UserStatusEnum.Enable ? UserStatusEnum.Disable.getCode() : UserStatusEnum.Enable.getCode();
        user.setStatus(newStatus);
        user.setModifyTime(new Date());
        userService.updateByIdFilter(user);
        return RestResponse.ok(newStatus);
    }


    @RequestMapping(value = "/delete/{id}", method = RequestMethod.POST)
    public RestResponse delete(@PathVariable Integer id) {
        User user = userService.getUserById(id);
        user.setDeleted(true);
        userService.updateByIdFilter(user);
        return RestResponse.ok();
    }


    @RequestMapping(value = "/selectByUserName", method = RequestMethod.POST)
    public RestResponse<List<KeyValue>> selectByUserName(@RequestBody String userName) {
        List<KeyValue> keyValues = userService.selectByUserName(userName);
        return RestResponse.ok(keyValues);
    }

}
